########################################################################
#  Searx-qt - Lightweight desktop application for SearX.
#  Copyright (C) 2020  CYBERDEViL
#
#  This file is part of Searx-qt.
#
#  Searx-qt is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Searx-qt is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
########################################################################

from PyQt5.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QFormLayout,
    QCheckBox,
    QLabel,
    QDoubleSpinBox,
    QLineEdit,
    QComboBox,
    QHBoxLayout,
    QSizePolicy,
    QTabWidget,
    QPlainTextEdit,
    QSpacerItem
)

from PyQt5.QtCore import Qt, pyqtSignal

from searxqt.widgets.buttons import Button

from searxqt.widgets.dialogs import UrlDialog

from searxqt.translations import _

HAVE_SOCKS = False
try:
    import socks
    HAVE_SOCKS = True
    del socks
except ImportError:
    print("pysocks not installed! No socks proxy support.")


class ProxyWidget(QWidget):
    changed = pyqtSignal(str)  # self.str()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        layout = QVBoxLayout(self)

        hLayout = QHBoxLayout()

        self._proxyType = QComboBox(self)
        self._proxyType.setSizePolicy(QSizePolicy(
            QSizePolicy.Maximum,
            QSizePolicy.Fixed))

        typeList = ['http']
        if HAVE_SOCKS:
            typeList += ['socks4', 'socks5']

        for item in typeList:
            self._proxyType.addItem(item)

        self._proxyStr = QLineEdit(self)
        self._proxyStr.setPlaceholderText("user:pass@host:port")

        hLayout.addWidget(self._proxyType)
        hLayout.addWidget(self._proxyStr)

        layout.addLayout(hLayout)

        self._proxyDns = QCheckBox(_("Proxy DNS"), self)
        layout.addWidget(self._proxyDns)

        if not HAVE_SOCKS:
            self._proxyDns.setToolTip(_("Install pysocks for socks support."))

        self._proxyDns.setEnabled(False)

        self._proxyStr.textChanged.connect(self.__changed)
        self._proxyType.currentIndexChanged.connect(self.__typeChanged)
        self._proxyDns.toggled.connect(self.__dnsChanged)

    def __changed(self):
        self.changed.emit(self.str())

    def __typeChanged(self, index):
        """ From proxy type combobox
        """
        if index == 0:
            self._proxyDns.setEnabled(False)
            self._proxyDns.setToolTip(_("Not available for http proxy."))
        else:
            self._proxyDns.setEnabled(True)

        if self.str():
            self.__changed()

    def __dnsChanged(self, state):
        """ From proxy dns checkbox
        """
        if self.str():
            self.__changed()

    def reset(self):
        self._proxyDns.setChecked(True)
        self._proxyStr.setText("")
        self._proxyType.setCurrentIndex(0)
        self.__typeChanged(0)

    def setStr(self, _str):
        self.reset()
        seq = _str.split(':')
        if len(seq) > 1:
            index = self._proxyType.findText(seq[0].rstrip('h'))
            if index != -1:
                self._proxyType.setCurrentIndex(index)
                self._proxyStr.setText(_str[len(seq[0]) + 3:])

            if seq[0] not in ['socks5h', 'socks4h']:
                self._proxyDns.setChecked(False)

    def str(self):
        if self._proxyStr.text():
            return "{0}://{1}".format(
                self.protocolStr(),
                self._proxyStr.text())
        return ""

    def protocol(self): return self._proxyType.currentText()

    def protocolStr(self):
        if (self.protocol() in ['socks4', 'socks5']
                and self._proxyDns.isChecked()):
            return "{0}h".format(self.protocol())

        return self.protocol()


class RequestsSettings(QWidget):
    def __init__(self, model, parent=None):
        """
        @param model:
        @type model: searxqt.models.RequestSettingsModel
        """
        QWidget.__init__(self, parent=parent)

        self._model = model

        layout = QFormLayout(self)

        # Verify checkbox
        self._verifyCheck = QCheckBox(self)
        layout.addRow(QLabel(_("Verify") + " (SSL):"), self._verifyCheck)

        # Timeout double spinbox
        self._timeoutSpin = QDoubleSpinBox(self)
        self._timeoutSpin.setSuffix(" sec")
        self._timeoutSpin.setMinimum(3)
        self._timeoutSpin.setMaximum(300)
        layout.addRow(QLabel(_("Timeout") + ":"), self._timeoutSpin)

        # Proxy
        proxyLayout = QFormLayout()
        layout.addRow(QLabel(_("Proxy") + ":"), proxyLayout)

        self._httpProxy = ProxyWidget(self)
        self._httpsProxy = ProxyWidget(self)

        proxyLayout.addRow(QLabel("Http:"), self._httpProxy)
        proxyLayout.addRow(QLabel("Https:"), self._httpsProxy)

        # Headers
        # User-agent
        userAgentLayout = QFormLayout()
        layout.addRow(QLabel(_("User-agents") + ":"), userAgentLayout)

        self._userAgentStringsEdit = QPlainTextEdit(self)
        self._userAgentStringsEdit.setToolTip(
            """- One user-agent string per line.
- Default user-agent string is the first (top) line.
- Empty lines will be removed.
- Leave empty to not send any user-agent string."""
        )
        userAgentLayout.addWidget(self._userAgentStringsEdit)

        self._userAgentEditButton = Button("", self)
        self._userAgentEditButton.setCheckable(True)
        userAgentLayout.addWidget(self._userAgentEditButton)

        self._randomUserAgent = QCheckBox(_("Random"), self)
        self._randomUserAgent.setToolTip(
            """When checked it will pick a random
user-agent from the list for each request."""
        )
        userAgentLayout.addWidget(self._randomUserAgent)

        # Init values for view
        self._changed()

        # Connections
        self._timeoutSpin.valueChanged.connect(self.__timeoutEdited)
        self._verifyCheck.stateChanged.connect(self.__verifyEdited)

        self._httpProxy.changed.connect(self.__proxyEdited)
        self._httpsProxy.changed.connect(self.__proxyEdited)

        self._userAgentEditButton.toggled.connect(self._toggleUserAgentEdit)
        self._randomUserAgent.stateChanged.connect(self._randomUserAgentEdited)

        self.__unsetWidgetsEditMode()

    def _randomUserAgentEdited(self, state):
        self._model.randomUserAgent = bool(state)

    def __setWidgetsEditMode(self):
        self._userAgentEditButton.setText(_("Save"))
        self._userAgentStringsEdit.setReadOnly(False)
        self._userAgentStringsEdit.setFocus()

    def __unsetWidgetsEditMode(self):
        self._userAgentEditButton.setText(_("Edit"))
        self._userAgentStringsEdit.setReadOnly(True)

    def _toggleUserAgentEdit(self, state):
        if state:
            self.__setWidgetsEditMode()
        else:
            self.__unsetWidgetsEditMode()
            self.__UserAgentStringsEdited(
                self._userAgentStringsEdit.toPlainText()
            )

    def __UserAgentStringsEdited(self, value):
        """
        @param value: String with the ?user-agent(s)
        @type value: str
        """
        self._model.useragents = [s for s in value.split('\n') if s]
        self._userAgentListChanged()

    def __timeoutEdited(self, value):
        self._model.timeout = value

    def __verifyEdited(self, state):
        self._model.verify = bool(state)

    def __proxyEdited(self, text):
        self._model.proxies = {
            'http': self._httpProxy.str(), 'https': self._httpsProxy.str()}

    def _userAgentListChanged(self):
        txt = ""
        for userAgentStr in self._model.useragents:
            if not txt:
                txt = userAgentStr
            else:
                txt += "\n{}".format(userAgentStr)
        self._userAgentStringsEdit.setPlainText(txt)

    def _changed(self):
        self._verifyCheck.setChecked(self._model.verify)
        self._timeoutSpin.setValue(self._model.timeout)

        self._httpProxy.setStr(self._model.proxies.get('http', 'socks5h://'))
        self._httpsProxy.setStr(self._model.proxies.get('https', 'socks5h://'))

        self._userAgentListChanged()
        self._randomUserAgent.setChecked(self._model.randomUserAgent)


class Stats2Settings(QWidget):
    def __init__(self, model, parent=None):
        """
        @type model: SearxStats2Model
        """
        QWidget.__init__(self, parent=parent)

        self._model = model

        layout = QVBoxLayout(self)

        infoLabel = QLabel(_(
            "The searx-stats2 project lists public searx instances with"
            " statistics. The original instance is running at"
            " https://searx.space/. This is where searx-qt will request"
            " a list with instances when the update button is pressed."),
            self
        )
        infoLabel.setWordWrap(True)

        layout.addWidget(infoLabel, 0, Qt.AlignTop)

        hLayout = QHBoxLayout()

        label = QLabel("URL:", self)
        label.setSizePolicy(
            QSizePolicy(
                QSizePolicy.Maximum, QSizePolicy.Maximum
            )
        )
        self._urlLabel = QLabel(model.url, self)
        self._urlEditButton = Button(_("Edit"), self)
        self._urlResetButton = Button(_("Reset"), self)

        hLayout.addWidget(label, 0, Qt.AlignTop)
        hLayout.addWidget(self._urlLabel, 0, Qt.AlignTop)
        hLayout.addWidget(self._urlEditButton, 0, Qt.AlignTop)
        hLayout.addWidget(self._urlResetButton, 0, Qt.AlignTop)

        spacer = QSpacerItem(
            20, 40, QSizePolicy.Minimum, QSizePolicy.MinimumExpanding
        )

        layout.addLayout(hLayout)
        layout.addItem(spacer)

        self._urlEditButton.clicked.connect(self.__urlEditClicked)
        self._urlResetButton.clicked.connect(self.__urlResetClicked)
        model.changed.connect(self.__modelChanged)

    def __modelChanged(self):
        self._urlLabel.setText(self._model.url)

    def __urlEditClicked(self):
        dialog = UrlDialog(self._model.url)
        if dialog.exec():
            self._model.url = dialog.url

    def __urlResetClicked(self):
        self._model.reset()


class SettingsWindow(QTabWidget):
    def __init__(self, model, parent=None):
        """
        @type model: SettingsModel
        """
        QTabWidget.__init__(self, parent=parent)
        self.setWindowTitle(_("Settings"))

        self._requestsView = RequestsSettings(model.requests, self)
        self.addTab(self._requestsView, _("Connection"))

        if model.stats2:
            self._stats2View = Stats2Settings(model.stats2, self)
            self.addTab(self._stats2View, "searx-stats2")
